﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Inovout.Data.Repository;
using Inovout.Serializer;
using Newtonsoft.Json;

namespace Inovout.Web.Http
{
    public class JsonNetMediaTypeFormatter : JsonMediaTypeFormatter
    {
        public JsonNetMediaTypeFormatter()
        {
            //解析XDomainRequest没有ContentType的问题。
            base.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
            base.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
            base.SerializerSettings.Converters.Add(new ComponentJsonConverter());
        }

        public override void WriteToStream(Type type, object value, Stream writeStream, Encoding effectiveEncoding)
        {
            using (JsonWriter jsonWriter = this.CreateJsonWriter(type, writeStream, effectiveEncoding))
            {
                jsonWriter.CloseOutput = false;
                var jsonSerializer = CreateJsonReader(type);
                jsonWriter.Formatting = jsonSerializer.Formatting;

                jsonSerializer.Serialize(jsonWriter, value, type);
                jsonWriter.Flush();
            }
        }
        private ConcurrentDictionary<Type, JsonSerializer> jsonSerializerCache = new ConcurrentDictionary<Type, JsonSerializer>();

        private JsonSerializer CreateJsonReader(Type type)
        {

            return
                 jsonSerializerCache.GetOrAdd(type, (t) =>
                 {
                     var jsonSerializer = this.CreateJsonSerializer();
                     jsonSerializer.ContractResolver = new NHibernateContractResolver(type);
                     return jsonSerializer;
                 });
        }

        public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger,
            CancellationToken cancellationToken)
        {

            if (content.Headers.ContentEncoding.Contains("gzip"))
            {
                using (GZipStream gzip = new GZipStream(readStream, CompressionMode.Decompress, true))
                {
                    return base.ReadFromStreamAsync(type, gzip, content, formatterLogger);
                }
            }
            else
            {
                return base.ReadFromStreamAsync(type, readStream, content, formatterLogger, cancellationToken);
            }
        }

        public override object ReadFromStream(Type type, Stream readStream, Encoding effectiveEncoding, IFormatterLogger formatterLogger)
        {
            using (JsonReader jsonReader = this.CreateJsonReader(type, readStream, effectiveEncoding))
            {
                jsonReader.CloseInput = false;
            
                var jsonSerializer = CreateJsonReader(type);
                EventHandler<Newtonsoft.Json.Serialization.ErrorEventArgs> errorHandler = null;
                if (formatterLogger != null)
                {
                    // Error must always be marked as handled
                    // Failure to do so can cause the exception to be rethrown at every recursive level and overflow the stack for x64 CLR processes
                    errorHandler = (sender, e) =>
                    {
                        Exception exception = e.ErrorContext.Error;
                        formatterLogger.LogError(e.ErrorContext.Path, exception);
                        e.ErrorContext.Handled = true;
                    };
                    jsonSerializer.Error += errorHandler;
                }
 
                try
                {
                    return jsonSerializer.Deserialize(jsonReader, type);
                }
                finally
                {
                    if (errorHandler != null)
                    {
                        // Clean up the error handler in case CreateJsonSerializer() reuses a serializer
                        jsonSerializer.Error -= errorHandler;
                    }
                }
            }
        }
    }
}
